/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package moteur.paquet;

import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;

/**
 * Abstraction d'un paquet d'entité, définie via un Enum, considéré comme désordonné et donc compté et tiré aléatoirement.
 * @param <T>
 * @author Administrateur
 */
public abstract class PaquetEnum<T extends Enum> {

    //private final Enum type;
    private final Type type;
    /**
     * Nombre de carte de chaque type dans le paquet.
     */
    protected byte[] paquet; //= new byte[ this.getClass().getEnumConstants().length ]; // suspect: class non enum...

    /**
     * Constructeur par defaut de cette classe au typage capillo-tracté. 
     * Met un type, tout en étant un gouffre à performance, mais propre. Peut-
     * être. Fonctionnement non élucidé à ce jour.
     */
    protected PaquetEnum() {
        Type superclass = getClass().getGenericSuperclass();
        if (superclass instanceof Class) {
            throw new RuntimeException("Missing type parameter.");
        }
        this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
    }

    /**
     * No Comment.
     * @return Type
     */
    public Type getType() {
        return this.type;
    }

    /**
     * @return total de carte ssi positif: sert à garder la pioche cohérente.
     * @throws PaquetVideException
     */
    private int totalNonNul() throws PaquetVideException {

        int accu = total();

        if (accu == 0) {
            throw new PaquetVideException();
        }
        return accu;
    }

    /**
     * @return le total de carte dans le paquet.
     * @throws PaquetVideException si le total est négatif.
     */
    public int total() throws PaquetVideException {

        int accu = 0;
        for (byte b : paquet) {
            if (b < 0) {
                throw new PaquetVideException();
            }
            accu += b;
        }

        return accu;
    }

    /**
     * Pioche un nombre n depuis "from"
     * @param n
     * @param from
     * @throws PaquetVideException
     * @throws TypeMismatchException
     */
    public void piocher(int n, PaquetEnum from) throws PaquetVideException, TypeMismatchException {

        if (!this.getType().equals(from.getType())) {
            throw new TypeMismatchException();
        }

        int taille = this.paquet.length;
        int total, tmp;

        total = from.totalNonNul(); //totalNonNul() doit gerer l'exception
        if (total < n) {
            throw new PaquetVideException();
        }

        for (int j = n; --j >= 0;) {

            tmp = (int) (Math.random() * total--);

            for (int i = 0; i < taille; i++) {
                tmp -= from.paquet[i];
                if (tmp < 0) {
                    from.paquet[i]--;
                    this.paquet[i]++;
                    break;
                }
            }
        }

    }

    /**
     * Pioche au hasard depuis "from"
     * @param from
     * @throws PaquetVideException
     * @throws TypeMismatchException
     */
    public void piocher(PaquetEnum from) throws PaquetVideException, TypeMismatchException {
        if (!this.getType().equals(from.getType())) {
            throw new TypeMismatchException();
        }

        int taille = this.paquet.length;
        int total = from.totalNonNul(); //total() doit gerer l'exception

        int tmp = (int) (Math.random() * total);
        for (int i = 0; i < taille; i++) {
            tmp -= from.paquet[i];
            if (tmp < 0) {
                from.paquet[i]--;
                this.paquet[i]++;
                break;
            }
        }
    }

    /**
     * ioche au hasard, et renvoie la valeur de l'enum du type pioché.
     * @param from
     * @return
     * @throws PaquetVideException
     * @throws TypeMismatchException
     */
    public int piocher2(PaquetEnum from) throws PaquetVideException, TypeMismatchException {
        if (!this.getType().equals(from.getType())) {
            throw new TypeMismatchException();
        }

        int taille = this.paquet.length;
        int total = from.totalNonNul(); //total() doit gerer l'exception

        int tmp = (int) (Math.random() * total);
        for (int i = 0; i < taille; i++) {
            tmp -= from.paquet[i];
            if (tmp < 0) {
                from.paquet[i]--;
                this.paquet[i]++;
                return i;
            }
        }
        assert false;
        return 0;
    }

    /**
     * Tranfer d'une unité de type enum depuis this jusqu'à destination.
     * @param e
     * @param dest
     * @throws TypeMismatchException
     * @throws PaquetVideException
     */
    public void transfer(Enum e, PaquetEnum dest) throws TypeMismatchException, PaquetVideException {
        if (!this.getType().equals(dest.getType())) {
            throw new TypeMismatchException();
        }
        if (paquet[e.ordinal()]-- > 0) {

            dest.paquet[e.ordinal()]++;
        } else {
            paquet[e.ordinal()]++;
            throw new PaquetVideException();
        }
    }

    /**
     * Tranfer d'un nombre nb de type enum depuis this jusqu'à destination.
     * @param e
     * @param nb
     * @param dest
     * @throws TypeMismatchException
     * @throws PaquetVideException
     */
    public void transferNb(Enum e, int nb, PaquetEnum dest) throws TypeMismatchException, PaquetVideException {
        for (int i = nb; --i>=0;){
            this.transfer(e, dest);
        }
    }

    /**
     * Vide le paquet dans un autre, préciser en paramêtre.
     * @param dest
     * @throws TypeMismatchException
     * @throws PaquetVideException
     */
    public void melanger(PaquetEnum dest) throws TypeMismatchException, PaquetVideException {
        if (!this.getType().equals(dest.getType())) {
            throw new TypeMismatchException();
        }

        for (int i = paquet.length; --i >= 0; ) {
            dest.paquet[i] += paquet[i];
            paquet[i] = 0;
        }

    }

    /**
     *
     * @param e
     * @return nombre d'enum dans le paquet.
     */
    public int getNb(Enum e){
        return paquet[e.ordinal()];
    }

    /**
     *
     * @return
     */
    public byte[] getPaquet() {
        return paquet;
    }
}
